TypeScript 98.3%
CSS 0.9%
Shell 0.7%
1// Panneau source d'une citation : extrait exact + contexte voisin, avec contrôle d'accès strict.2import { NextResponse } from "next/server";3import { apiError } from "@/lib/api.ts";4import { requireUser } from "@/lib/auth/session.ts";5import { all, get } from "@/lib/db/index.ts";67export async function GET(_req: Request, ctx: { params: Promise<{ chunkId: string }> }) {8 try {9 const user = await requireUser();10 const id = parseInt((await ctx.params).chunkId, 10);11 const chunk = get<{12 id: number; document_id: number; course_code: string | null; space: string; ref_type: string;13 ref_number: number | null; ref_label: string; section_title: string; title: string;14 display_content: string; week: number | null; owner_user_id: number | null;15 doc_title: string; filename: string; path: string; ingested_at: string | null;16 }>(17 `SELECT c.id, c.document_id, c.course_code, c.space, c.ref_type, c.ref_number, c.ref_label,18 c.section_title, c.title, c.display_content, c.week, c.owner_user_id,19 d.title as doc_title, d.filename, d.path, d.ingested_at20 FROM chunks c JOIN documents d ON d.id = c.document_id WHERE c.id = ?`,21 id22 );23 if (!chunk) return NextResponse.json({ error: "Source introuvable." }, { status: 404 });2425 // Contrôle d'accès : jamais l'espace professeur pour un étudiant ; les espaces étudiants26 // uniquement pour leur propriétaire ; les cours officiels selon l'inscription.27 if (chunk.space === "instructor-private" && user.role === "student") {28 return NextResponse.json({ error: "Accès refusé." }, { status: 403 });29 }30 if (chunk.space.startsWith("student-") && chunk.owner_user_id !== user.id) {31 return NextResponse.json({ error: "Accès refusé." }, { status: 403 });32 }33 if (chunk.space.startsWith("official-") && chunk.course_code) {34 const enrolled = get("SELECT 1 as ok FROM enrollments WHERE user_id = ? AND course_code = ?", user.id, chunk.course_code);35 if (!enrolled && user.role === "student") return NextResponse.json({ error: "Accès refusé." }, { status: 403 });36 }3738 const neighbors = chunk.ref_number != null39 ? all(40 `SELECT id, ref_number, title, substr(display_content, 1, 400) as preview FROM chunks41 WHERE document_id = ? AND ref_number IN (?, ?) AND id != ? ORDER BY ref_number`,42 chunk.document_id, (chunk.ref_number ?? 0) - 1, (chunk.ref_number ?? 0) + 1, chunk.id43 )44 : [];4546 return NextResponse.json({ source: chunk, neighbors });47 } catch (e) {48 return apiError(e);49 }50}51